#include "stdafx.h"
#pragma hdrstop

#include "MessageHandler.h"
#include "LengthField.h"
#include "P3Error.h"

#include "Signal.h"
#include "Command.h"
#include "Status.h"

#include <sstream>
#include <iomanip>
#include <cstdio>

using namespace std;

// A macro that contains the structure of a single case statement in the
// main message handling function.
#define CONSIDER(x)                                                      \
  case Header<x>::descSupp:                                              \
  Handle##x( iss );                                                    \
  CheckForError( iss, #x );                                            \
  break;

// Main message handling functions.
void MessageHandler::HandleMessage( MessageQueue& ioQueue )
{
	if( ioQueue.Empty() )
		return;

	MessageQueueEntry entry = ioQueue.Next();

	istringstream iss( string( entry.message, entry.length ) );
	delete[] entry.message;
	switch( entry.descSupp )
	{
		CONSIDER( Signal );
		CONSIDER( Command );
	default:
		p3err_ << ": Unknown message descriptor/supplement 0x"
			<< hex << entry.descSupp << endl;
	}
}

void MessageHandler::CheckForError( istream& inStream, const char* inMessageType )
{
	if( inStream.fail() || inStream.peek() != EOF )
		p3err_ << ": Error reading " << inMessageType << " message" << endl;
}

void MessageHandler::HandleMessage( istream& is )
{
	MessageQueue queue;
	queue.QueueMessage( is );
	HandleMessage( queue );
}

// Functions that construct messages.

// Generic implementation.
template<typename content_type>
ostream& MessageHandler::PutMessage( ostream& os, const content_type& content )
{
	os.put( char( Header<content_type>::descSupp >> 8 ) ); // the old-style cast will silence MSVC's irrelevant C4309 warning
	os.put( char( Header<content_type>::descSupp & 0xff ) );
	ostringstream buffer;
	content.WriteBinary( buffer );
	string str = buffer.str();
	LengthField<2> length = str.length();
	length.WriteBinary( os );
	os.write( str.data(), length );
	return os;
}


// Enforce instantiation of all message construction functions here,
// i.e. in this compilation unit.
template ostream& MessageHandler::PutMessage( std::ostream&, const Signal& );
template ostream& MessageHandler::PutMessage( std::ostream&, const Command& );
template ostream& MessageHandler::PutMessage( std::ostream&, const Status& );
